/*
 * jxDao Project (2014)
 * 
 * by Denilson Edinaldo Pinto
 * 
 * LGPL
 * Copyright (C) 2014
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.jxDao.dao.filter;

import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.jxUtils.ReflectionUtil;
import org.jxUtils.TypeUtil;
import org.jxUtils.check.CheckUtil;
import org.jxUtils.check.ParameterCheck;
import org.jxUtils.collection.CollectionUtil;
import org.jxUtils.date.DateUtil;

/**
 * @author Denilson Edinaldo Pinto
 * @porject jxDao
 * @package org.jxDao.dao.filter
 * @date 11/04/2014 - 10:56:51
 */
class Filter implements Filterable {
	
	private static final long serialVersionUID = 6174459822507424326L;
	
	protected String columnName;
	
	protected FilterOperator operator;
	
	protected List<Object> values = new ArrayList<>();
	
	private GroupBy groupBy = null;
	
	private OrderBy orderBy = null;
	
	private boolean isMD5Value = false;
	
	/**
	 * @param columnName
	 * @param operator
	 * @param value
	 */
	public Filter(final String columnName, final FilterOperator operator, final Object value) {
	
		this(columnName, operator, value, false);
	}
	
	/**
	 * @param columnName
	 * @param operator
	 * @param value
	 * @param isMD5Value
	 */
	public Filter(final String columnName, final FilterOperator operator, final Object value, final boolean isMD5Value) {
	
		ParameterCheck.isNull(operator, "operator");
		
		//
		if( !operator.equals(FilterOperator.AND) && !operator.equals(FilterOperator.OR) && !operator.equals(FilterOperator.CUSTOM)) {
			
			ParameterCheck.isNullOrAbsoluteEmpty(columnName, "columnName");
			
			//
			if( !operator.equals(FilterOperator.NULL) && !operator.equals(FilterOperator.NOT_NULL) && !operator.equals(FilterOperator.EQUAL) && !operator.equals(FilterOperator.NOT_EQUAL)) {
				
				ParameterCheck.isNullOrEmpty(value, "value");
			}
		}
		
		this.columnName = columnName;
		this.operator = operator;
		this.isMD5Value = isMD5Value;
		
		values.add(value);
	}
	
	/**
	 * @return the columnName
	 */
	@Override
	public final String getColumnName() {
	
		return columnName;
	}
	
	/**
	 * @return the operator
	 */
	@Override
	public final FilterOperator getOperator() {
	
		return operator;
	}
	
	/**
	 * @return the Value Object or Values List
	 */
	@Override
	public final Object getValue() {
	
		return values.size() == 1 ? values.get(0) : values;
	}
	
	/**
	 * @return the groupBy
	 */
	@Override
	public final GroupBy getGroupBy() {
	
		return groupBy;
	}
	
	/**
	 * @return the orderBy
	 */
	@Override
	public final OrderBy getOrderBy() {
	
		return orderBy;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxDao.dao.filter.Filterable#groupBy(org.jxDao.dao.filter.GroupBy)
	 */@Override
	public final Filterable groupBy(final GroupBy groupBy) {
	
		this.groupBy = groupBy;
		
		return this;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxDao.dao.filter.Filterable#orderBy(org.jxDao.dao.filter.OrderBy)
	 */@Override
	public final Filterable orderBy(final OrderBy orderBy) {
	
		this.orderBy = orderBy;
		
		return this;
	}
	
	/**
	 * @param filters
	 * @return this + filters
	 */
	@Override
	public final Filterable and(final Filterable... filters) {
	
		ParameterCheck.isNullOrEmpty(filters, "filters");
		
		values.add(new Filter("", FilterOperator.AND, ""));
		
		//
		for(final Filterable filter : filters) {
			
			values.add(filter);
		}
		
		return this;
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andBetween(final String columnName, final Date start, final Date end) {
	
		return and(new BetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andBetween(final String columnName, final Number start, final Number end) {
	
		return and(new BetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andBetween(final String columnName, final Object start, final Object end) {
	
		return and(new BetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andBetween(final String columnName, final Time start, final Time end) {
	
		return and(new BetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andBetween(final String columnName, final Timestamp start, final Timestamp end) {
	
		return and(new BetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param sql
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andCustom(final String sql) {
	
		return and(new CustomFilter(sql));
	}
	
	/**
	 * @param sql
	 * @param groupBy
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andCustom(final String sql, final GroupBy groupBy) {
	
		return and(new CustomFilter(sql, groupBy));
	}
	
	/**
	 * @param sql
	 * @param groupBy
	 * @param orderBy
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andCustom(final String sql, final GroupBy groupBy, final OrderBy orderBy) {
	
		return and(new CustomFilter(sql, groupBy, orderBy));
	}
	
	/**
	 * @param sql
	 * @param orderBy
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andCustom(final String sql, final OrderBy orderBy) {
	
		return and(new CustomFilter(sql, orderBy));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andEqual(final String columnName, final Object value) {
	
		return and(new EqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @param isMD5Value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andEqual(final String columnName, final Object value, final boolean isMD5Value) {
	
		return and(new EqualFilter(columnName, value, isMD5Value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andGreaterOrEqual(final String columnName, final Date value) {
	
		return and(new GreaterOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andGreaterOrEqual(final String columnName, final Number value) {
	
		return and(new GreaterOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andGreaterOrEqual(final String columnName, final Object value) {
	
		return and(new GreaterOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andGreaterOrEqual(final String columnName, final Time value) {
	
		return and(new GreaterOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andGreaterOrEqual(final String columnName, final Timestamp value) {
	
		return and(new GreaterOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andGreaterThan(final String columnName, final Date value) {
	
		return and(new GreaterThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andGreaterThan(final String columnName, final Number value) {
	
		return and(new GreaterThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andGreaterThan(final String columnName, final Object value) {
	
		return and(new GreaterThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andGreaterThan(final String columnName, final Time value) {
	
		return and(new GreaterThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andGreaterThan(final String columnName, final Timestamp value) {
	
		return and(new GreaterThanFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE UPPER('%value%')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andILikeBetween(final String columnName, final String value) {
	
		return and(new ILikeBetweenFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE UPPER('%value')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andILikeEnd(final String columnName, final String value) {
	
		return and(new ILikeEndFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE UPPER('value')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andILikeEqual(final String columnName, final String value) {
	
		return and(new ILikeEqualFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE UPPER('value%')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andILikeStart(final String columnName, final String value) {
	
		return and(new ILikeStartFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param values
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andIn(final String columnName, final Collection<?> values) {
	
		return and(new InFilter(columnName, values));
	}
	
	/**
	 * @param columnName
	 * @param values
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andIn(final String columnName, final Object[] values) {
	
		return and(new InFilter(columnName, values));
	}
	
	/**
	 * @param columnName
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andIsNotNull(final String columnName) {
	
		return and(new IsNotNullFilter(columnName));
	}
	
	/**
	 * @param columnName
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andIsNull(final String columnName) {
	
		return and(new IsNullFilter(columnName));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andLessOrEqual(final String columnName, final Date value) {
	
		return and(new LessOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andLessOrEqual(final String columnName, final Number value) {
	
		return and(new LessOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andLessOrEqual(final String columnName, final Object value) {
	
		return and(new LessOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andLessOrEqual(final String columnName, final Time value) {
	
		return and(new LessOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andLessOrEqual(final String columnName, final Timestamp value) {
	
		return and(new LessOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andLessThan(final String columnName, final Date value) {
	
		return and(new LessThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andLessThan(final String columnName, final Number value) {
	
		return and(new LessThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andLessThan(final String columnName, final Object value) {
	
		return and(new LessThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andLessThan(final String columnName, final Time value) {
	
		return and(new LessThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andLessThan(final String columnName, final Timestamp value) {
	
		return and(new LessThanFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE '%value%'
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andLikeBetween(final String columnName, final String value) {
	
		return and(new LikeBetweenFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE '%value'
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andLikeEnd(final String columnName, final String value) {
	
		return and(new LikeEndFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE 'value'
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andLikeEqual(final String columnName, final String value) {
	
		return and(new LikeEqualFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE 'value%'
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andLikeStart(final String columnName, final String value) {
	
		return and(new LikeStartFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotBetween(final String columnName, final Date start, final Date end) {
	
		return and(new NotBetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotBetween(final String columnName, final Number start, final Number end) {
	
		return and(new NotBetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotBetween(final String columnName, final Object start, final Object end) {
	
		return and(new NotBetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotBetween(final String columnName, final Time start, final Time end) {
	
		return and(new NotBetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotBetween(final String columnName, final Timestamp start, final Timestamp end) {
	
		return and(new NotBetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotEqual(final String columnName, final Object value) {
	
		return and(new NotEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @param isMD5Value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotEqual(final String columnName, final Object value, final boolean isMD5Value) {
	
		return and(new NotEqualFilter(columnName, value, isMD5Value));
	}
	
	/**
	 * columnName NOT LIKE UPPER('%value%')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotILikeBetween(final String columnName, final String value) {
	
		return and(new NotILikeBetweenFilter(columnName, value));
	}
	
	/**
	 * columnName NOT LIKE UPPER('%value')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotILikeEnd(final String columnName, final String value) {
	
		return and(new NotILikeEndFilter(columnName, value));
	}
	
	/**
	 * columnName NOT LIKE UPPER('value')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotILikeEqual(final String columnName, final String value) {
	
		return and(new NotILikeEqualFilter(columnName, value));
	}
	
	/**
	 * columnName NOT LIKE UPPER('value%')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotILikeStart(final String columnName, final String value) {
	
		return and(new NotILikeStartFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param values
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotIn(final String columnName, final Collection<?> values) {
	
		return and(new NotInFilter(columnName, values));
	}
	
	/**
	 * @param columnName
	 * @param values
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotIn(final String columnName, final Object[] values) {
	
		return and(new NotInFilter(columnName, values));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotLikeBetween(final String columnName, final String value) {
	
		return and(new NotLikeBetweenFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotLikeEnd(final String columnName, final String value) {
	
		return and(new NotLikeEndFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotLikeEqual(final String columnName, final String value) {
	
		return and(new NotLikeEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable andNotLikeStart(final String columnName, final String value) {
	
		return and(new NotLikeStartFilter(columnName, value));
	}
	
	/**
	 * @param filters
	 * @return this + filters
	 */
	@Override
	public final Filterable or(final Filterable... filters) {
	
		ParameterCheck.isNullOrEmpty(filters, "filters");
		
		values.add(new Filter("", FilterOperator.OR, ""));
		
		//
		for(final Filterable filter : filters) {
			
			values.add(filter);
		}
		
		return this;
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orBetween(final String columnName, final Date start, final Date end) {
	
		return or(new BetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orBetween(final String columnName, final Number start, final Number end) {
	
		return or(new BetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orBetween(final String columnName, final Object start, final Object end) {
	
		return or(new BetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orBetween(final String columnName, final Time start, final Time end) {
	
		return or(new BetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orBetween(final String columnName, final Timestamp start, final Timestamp end) {
	
		return or(new BetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param sql
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orCustom(final String sql) {
	
		return or(new CustomFilter(sql));
	}
	
	/**
	 * @param sql
	 * @param groupBy
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orCustom(final String sql, final GroupBy groupBy) {
	
		return or(new CustomFilter(sql, groupBy));
	}
	
	/**
	 * @param sql
	 * @param groupBy
	 * @param orderBy
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orCustom(final String sql, final GroupBy groupBy, final OrderBy orderBy) {
	
		return or(new CustomFilter(sql, groupBy, orderBy));
	}
	
	/**
	 * @param sql
	 * @param orderBy
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orCustom(final String sql, final OrderBy orderBy) {
	
		return or(new CustomFilter(sql, orderBy));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orEqual(final String columnName, final Object value) {
	
		return or(new EqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @param isMD5Value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orEqual(final String columnName, final Object value, final boolean isMD5Value) {
	
		return or(new EqualFilter(columnName, value, isMD5Value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orGreaterOrEqual(final String columnName, final Date value) {
	
		return or(new GreaterOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orGreaterOrEqual(final String columnName, final Number value) {
	
		return or(new GreaterOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orGreaterOrEqual(final String columnName, final Object value) {
	
		return or(new GreaterOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orGreaterOrEqual(final String columnName, final Time value) {
	
		return or(new GreaterOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orGreaterOrEqual(final String columnName, final Timestamp value) {
	
		return or(new GreaterOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orGreaterThan(final String columnName, final Date value) {
	
		return or(new GreaterThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orGreaterThan(final String columnName, final Number value) {
	
		return or(new GreaterThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orGreaterThan(final String columnName, final Object value) {
	
		return or(new GreaterThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orGreaterThan(final String columnName, final Time value) {
	
		return or(new GreaterThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orGreaterThan(final String columnName, final Timestamp value) {
	
		return or(new GreaterThanFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE UPPER('%value%')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orILikeBetween(final String columnName, final String value) {
	
		return or(new ILikeBetweenFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE UPPER('%value')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orILikeEnd(final String columnName, final String value) {
	
		return or(new ILikeEndFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE UPPER('value')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orILikeEqual(final String columnName, final String value) {
	
		return or(new ILikeEqualFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE UPPER('value%')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orILikeStart(final String columnName, final String value) {
	
		return or(new ILikeStartFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param values
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orIn(final String columnName, final Collection<?> values) {
	
		return or(new InFilter(columnName, values));
	}
	
	/**
	 * @param columnName
	 * @param values
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orIn(final String columnName, final Object[] values) {
	
		return or(new InFilter(columnName, values));
	}
	
	/**
	 * @param columnName
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orIsNotNull(final String columnName) {
	
		return or(new IsNotNullFilter(columnName));
	}
	
	/**
	 * @param columnName
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orIsNull(final String columnName) {
	
		return or(new IsNullFilter(columnName));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orLessOrEqual(final String columnName, final Date value) {
	
		return or(new LessOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orLessOrEqual(final String columnName, final Number value) {
	
		return or(new LessOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orLessOrEqual(final String columnName, final Object value) {
	
		return or(new LessOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orLessOrEqual(final String columnName, final Time value) {
	
		return or(new LessOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orLessOrEqual(final String columnName, final Timestamp value) {
	
		return or(new LessOrEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orLessThan(final String columnName, final Date value) {
	
		return or(new LessThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orLessThan(final String columnName, final Number value) {
	
		return or(new LessThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orLessThan(final String columnName, final Object value) {
	
		return or(new LessThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orLessThan(final String columnName, final Time value) {
	
		return or(new LessThanFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orLessThan(final String columnName, final Timestamp value) {
	
		return or(new LessThanFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE '%value%'
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orLikeBetween(final String columnName, final String value) {
	
		return or(new LikeBetweenFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE '%value'
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orLikeEnd(final String columnName, final String value) {
	
		return or(new LikeEndFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE 'value'
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orLikeEqual(final String columnName, final String value) {
	
		return or(new LikeEqualFilter(columnName, value));
	}
	
	/**
	 * columnName LIKE 'value%'
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orLikeStart(final String columnName, final String value) {
	
		return or(new LikeStartFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotBetween(final String columnName, final Date start, final Date end) {
	
		return or(new NotBetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotBetween(final String columnName, final Number start, final Number end) {
	
		return or(new NotBetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotBetween(final String columnName, final Object start, final Object end) {
	
		return or(new NotBetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotBetween(final String columnName, final Time start, final Time end) {
	
		return or(new NotBetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param start
	 * @param end
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotBetween(final String columnName, final Timestamp start, final Timestamp end) {
	
		return or(new NotBetweenFilter(columnName, start, end));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotEqual(final String columnName, final Object value) {
	
		return or(new NotEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @param isMD5Value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotEqual(final String columnName, final Object value, final boolean isMD5Value) {
	
		return or(new NotEqualFilter(columnName, value, isMD5Value));
	}
	
	/**
	 * columnName NOT LIKE UPPER('%value%')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotILikeBetween(final String columnName, final String value) {
	
		return or(new NotILikeBetweenFilter(columnName, value));
	}
	
	/**
	 * columnName NOT LIKE UPPER('%value')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotILikeEnd(final String columnName, final String value) {
	
		return or(new NotILikeEndFilter(columnName, value));
	}
	
	/**
	 * columnName NOT LIKE UPPER('value')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotILikeEqual(final String columnName, final String value) {
	
		return or(new NotILikeEqualFilter(columnName, value));
	}
	
	/**
	 * columnName NOT LIKE UPPER('value%')
	 * 
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotILikeStart(final String columnName, final String value) {
	
		return or(new NotILikeStartFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param values
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotIn(final String columnName, final Collection<?> values) {
	
		return or(new NotInFilter(columnName, values));
	}
	
	/**
	 * @param columnName
	 * @param values
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotIn(final String columnName, final Object[] values) {
	
		return or(new NotInFilter(columnName, values));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotLikeBetween(final String columnName, final String value) {
	
		return or(new NotLikeBetweenFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotLikeEnd(final String columnName, final String value) {
	
		return or(new NotLikeEndFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotLikeEqual(final String columnName, final String value) {
	
		return or(new NotLikeEqualFilter(columnName, value));
	}
	
	/**
	 * @param columnName
	 * @param value
	 * @return {@link Filterable}
	 */
	@Override
	public final Filterable orNotLikeStart(final String columnName, final String value) {
	
		return or(new NotLikeStartFilter(columnName, value));
	}
	
	/**
	 * @param value
	 * @return result String
	 */
	private String toString(final Filter filter) {
	
		final StringBuilder result = new StringBuilder();
		
		final FilterOperator operator = filter.getOperator();
		
		//
		if(operator.equals(FilterOperator.NULL) || operator.equals(FilterOperator.NOT_NULL)) {
			
			result.append(" " + filter.getColumnName() + " " + operator);
			
		}else if(operator.equals(FilterOperator.BETWEEN)) {
			
			final Object[] values = (Object[])filter.getValue();
			
			result.append(" " + filter.getColumnName());
			result.append(" " + operator);
			result.append(" " + valueToString(values[0], filter.isMD5Value));
			result.append(" " + FilterOperator.AND);
			result.append(" " + valueToString(values[1], filter.isMD5Value));
			
		}else if(operator.equals(FilterOperator.CUSTOM)) {
			
			//
			if(filter.getValue() != null) {
				
				String value = filter.getValue().toString();
				
				//
				if(CheckUtil.isNotAbsoluteEmpty(value)) {
					
					result.append(" " + value.trim() + " ");
				}
			}
			
		}else {
			
			//
			if(operator.equals(FilterOperator.ILIKE) || operator.equals(FilterOperator.NOT_ILIKE)) {
				
				result.append(" UPPER(" + filter.getColumnName() + ")");
				
			}else {
				
				result.append(" " + filter.getColumnName());
			}
			
			//
			if(operator.equals(FilterOperator.AND) || operator.equals(FilterOperator.OR)) {
				
				result.append("\n    ");
				result.append(operator);
				
			}else if(operator.equals(FilterOperator.IN) || operator.equals(FilterOperator.NOT_IN)) {
				
				final Collection<?> collection = filter.getValue() instanceof Collection ? (Collection<?>)filter.getValue() : Arrays.asList((Object[])filter.getValue());
				
				//
				if( !collection.isEmpty()) {
					
					result.append(" ");
					result.append(operator);
					result.append(" (");
					
					//
					final Iterator<?> iterator = collection.iterator();
					
					result.append(valueToString(iterator.next(), filter.isMD5Value));
					
					//
					while(iterator.hasNext()) {
						
						result.append(", " + valueToString(iterator.next(), filter.isMD5Value));
					}
					
					result.append(")");
				}
				
			}else {
				
				result.append(" ");
				result.append(operator);
				
				final String strValue = valueToString(filter.getValue(), filter.isMD5Value);
				
				//
				if(operator.equals(FilterOperator.ILIKE) || operator.equals(FilterOperator.NOT_ILIKE)) {
					
					result.append("(" + strValue + ")");
					
				}else {
					
					result.append(" ");
					result.append(strValue);
				}
			}
		}
		
		return result.toString();
	}
	
	/**
	 * @param value
	 * @param isMD5Value
	 * @return result String
	 */
	private String valueToString(final Object value, final boolean isMD5Value) {
	
		//
		if(value == null) {
			
			return "NULL";
			
		}else if(TypeUtil.isEnum(value) || value instanceof CharSequence) {
			
			return isMD5Value ? "MD5('" + value + "')" : "'" + value + "'";
			
		}else if(TypeUtil.isDateOrTime(value)) {
			
			//
			if(value instanceof java.sql.Time) {
				
				return "'" + DateUtil.parse((java.sql.Time)value, "HH:mm:ss") + "'";
				
			}else if(value instanceof java.sql.Timestamp) {
				
				return "'" + DateUtil.parse((java.sql.Timestamp)value, "HH:mm:ss.SSS") + "'";
				
			}else if(value instanceof java.sql.Date) {
				
				return "'" + DateUtil.parse((java.sql.Date)value, "yyyy-MM-dd") + "'";
				
			}else if(value instanceof Date) {
				
				return "'" + DateUtil.parse((Date)value, "yyyy-MM-dd HH:mm:ss.SSS") + "'";
				
			}else {
				
				return "'" + DateUtil.parse(((Calendar)value).getTime(), "yyyy-MM-dd HH:mm:ss.SSS") + "'";
			}
			
		}else if(TypeUtil.isCollection(value) || TypeUtil.isArray(value)) {
			
			final Collection<?> collection = CollectionUtil.toList(value);
			
			final StringBuilder strValue = new StringBuilder();
			
			//
			for(final Object object : collection) {
				
				strValue.append(object instanceof Filter ? toString((Filter)object) : valueToString(object, isMD5Value));
			}
			
			return strValue.toString();
			
		}else {
			
			return String.valueOf(value);
		}
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */@Override
	public String toString() {
	
		String str = toString(this);
		
		str = str.concat(getGroupBy() != null ? "\n" + getGroupBy() : "");
		str = str.concat(getOrderBy() != null ? "\n" + getOrderBy() : "");
		str = str.replaceAll("[\n]{1}[ ]{4,}", "\n\t");
		str = str.replaceAll("[ ]{2,}", " ");
		str = str.replaceAll("[\n]{1}[\t]{1,}[ ]{1,}", "\n\t");
		
		return str.trim();
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#clone()
	 */@Override
	public Filterable clone() {
	
		return ReflectionUtil.clone(this);
	}
}
